// Magic Software, Inc.
// http://www.magic-software.com
// Copyright (c) 2000, All Rights Reserved
//
// Source code from Magic Software is supplied under the terms of a license
// agreement and may not be copied or disclosed except in accordance with the
// terms of that agreement.  The various license agreements may be found at
// the Magic Software web site.  This file is subject to the license
//
// FREE SOURCE CODE
// http://www.magic-software.com/License/free.pdf

#include <windows.h>
#include <mmsystem.h>

#include "MgcApplication.h"
#include "MgcCommand.h"
#include <MgcOglRenderer.pkg>

using namespace Mgc;

const int MgcApplication::KEY_ESCAPE = VK_ESCAPE;
const int MgcApplication::KEY_LEFT_ARROW = VK_LEFT;
const int MgcApplication::KEY_RIGHT_ARROW = VK_RIGHT;
const int MgcApplication::KEY_UP_ARROW = VK_UP;
const int MgcApplication::KEY_DOWN_ARROW = VK_DOWN;
const int MgcApplication::KEY_HOME = VK_HOME;
const int MgcApplication::KEY_END = VK_END;
const int MgcApplication::KEY_PAGE_UP = VK_PRIOR;
const int MgcApplication::KEY_PAGE_DOWN = VK_NEXT;
const int MgcApplication::KEY_INSERT = VK_INSERT;
const int MgcApplication::KEY_DELETE = VK_DELETE;
const int MgcApplication::KEY_F1 = VK_F1;
const int MgcApplication::KEY_F2 = VK_F2;
const int MgcApplication::KEY_F3 = VK_F3;
const int MgcApplication::KEY_F4 = VK_F4;
const int MgcApplication::KEY_F5 = VK_F5;
const int MgcApplication::KEY_F6 = VK_F6;
const int MgcApplication::KEY_F7 = VK_F7;
const int MgcApplication::KEY_F8 = VK_F8;
const int MgcApplication::KEY_F9 = VK_F9;
const int MgcApplication::KEY_F10 = VK_F10;
const int MgcApplication::KEY_F11 = VK_F11;
const int MgcApplication::KEY_F12 = VK_F12;

const int MgcApplication::MGC_MOUSE_LEFT_BUTTON = 0;
const int MgcApplication::MGC_MOUSE_MIDDLE_BUTTON = 1;
const int MgcApplication::MGC_MOUSE_RIGHT_BUTTON = 2;
const int MgcApplication::MOUSE_UP = 0;
const int MgcApplication::MOUSE_DOWN = 1;

#pragma comment(lib,"comctl32.lib")
#pragma comment(lib,"winmm.lib")
#pragma comment(lib,"opengl32.lib")
#pragma comment(lib,"glu32.lib")
#pragma comment(lib,"MgcCore.lib")
#pragma comment(lib,"MgcEngine.lib")
#pragma comment(lib,"MgcOglRenderer.lib")

//---------------------------------------------------------------------------
void MgcApplication::RequestTermination ()
{
    PostMessage((HWND)ms_iWindowID,WM_DESTROY,0,0);
}
//----------------------------------------------------------------------------
MgcReal MgcApplication::GetTimeInSeconds ()
{
    MgcReal fTime = 0.001*timeGetTime();
    return fTime;
}
//----------------------------------------------------------------------------
void MgcApplication::MoveCamera ()
{
    // GLUT does not have distinct callbacks for key-down versus key-up.  So
    // I have to reset the 'key down' flags each time.  This leads to not so
    // smooth camera motion.  For Windows, we don't have this problem.

    if ( m_bUArrowPressed )
        MoveForward();
    else if ( m_bDArrowPressed )
        MoveBackward();

    if ( m_bHomePressed )
        MoveUp();
    else if ( m_bEndPressed )
        MoveDown();

    if ( m_bLArrowPressed )
        TurnLeft();
    else if ( m_bRArrowPressed )
        TurnRight();

    if ( m_bPgUpPressed )
        LookUp();
    else if ( m_bPgDnPressed )
        LookDown();
}
//----------------------------------------------------------------------------
static char* ProcessCommand (char* acArgument)
{
    int iLength = strlen(acArgument);

    // strip off quotes if command line was built from double-clicking a file
    char* acProcessed = new char[iLength+1];
	if ( acArgument[0] == '\"' )
    {
		strcpy_s(acProcessed,iLength+1,acArgument+1);  // remove leading quote
		if ( acArgument[iLength-1] == '\"' )
			acProcessed[iLength-2] = '\0';  // remove trailing quote
	}
	else
    {
		strcpy_s(acProcessed,iLength+1,acArgument);
    }

    return acProcessed;
}
//---------------------------------------------------------------------------
LRESULT CALLBACK WinProc (HWND hWnd, UINT uiMsg, WPARAM wParam,
    LPARAM lParam)
{
    // GLUT does not support callbacks for key-down and key-up events.  As
    // such, it is not possible to get smooth camera motion with the
    // turret-based system while using GLUT.  However, Windows does have
    // key-down and key-up messages.  The following hack is used to allow
    // a Windows-based application to get the smooth motion.
    //    
    // The key-pressed callbacks require the mouse coordinates to be passed
    // as a pair of integers.  For Windows, the low-order byte of the x value
    // will contain the mouse x location.  The high-order byte will contain
    // either a 0 (WM_CHAR), 1 (WM_KEYDOWN), or 2 (WM_KEYUP).  The y value
    // contains the mouse location without encoding.

    MgcApplication* pkTheApp = MgcApplication::GetApplication();
    if ( !pkTheApp )
        return DefWindowProc(hWnd,uiMsg,wParam,lParam);

    switch ( uiMsg ) 
    {
        case WM_PAINT:
        {
            PAINTSTRUCT kPS;
            HDC hDC = BeginPaint(hWnd,&kPS);
            pkTheApp->OnDisplay();
            EndPaint(hWnd,&kPS);
            return 0;
        }
        case WM_MOVE:
        {
            int iXPos = int(LOWORD(lParam));
            int iYPos = int(HIWORD(lParam));
            pkTheApp->OnMotion(iXPos,iYPos);
            return 0;
        }
        case WM_SIZE:
        {
            int iWidth = int(LOWORD(lParam));
            int iHeight = int(HIWORD(lParam));
            pkTheApp->OnReshape(iWidth,iHeight);
            return 0;
        }
        case WM_CHAR:
        {
            unsigned char ucKey = (unsigned char)(char)wParam;
            POINT kPoint;
            GetCursorPos(&kPoint);
            int iXPos = (int) kPoint.x;
            int iYPos = (int) kPoint.y;
            pkTheApp->OnKeyPress(ucKey,iXPos,iYPos);
            return 0;
        }
        case WM_KEYDOWN:
        {
            int iVirtKey = int(wParam);
            if ( (VK_F1 <= iVirtKey && iVirtKey <= VK_F12)
            ||   (VK_PRIOR <= iVirtKey && iVirtKey <= VK_DOWN)
            ||   (iVirtKey == VK_INSERT) )
            {
                pkTheApp->OnSpecialKeyDown(iVirtKey);
            }
            else
            {
                pkTheApp->OnKeyDown(iVirtKey);
            }
            return 0;
        }
        case WM_KEYUP:
        {
            int iVirtKey = int(wParam);
            if ( (VK_F1 <= iVirtKey && iVirtKey <= VK_F12)
            ||   (VK_PRIOR <= iVirtKey && iVirtKey <= VK_DOWN)
            ||   (iVirtKey == VK_INSERT) )
            {
                pkTheApp->OnSpecialKeyUp(iVirtKey);
            }
            else
            {
                pkTheApp->OnKeyUp(iVirtKey);
            }
            return 0;
        }
        case WM_LBUTTONDOWN:
        {
            int iXPos = int(LOWORD(lParam));
            int iYPos = int(HIWORD(lParam));
            pkTheApp->OnMousePress(MgcApplication::MGC_MOUSE_LEFT_BUTTON,
                MgcApplication::MOUSE_DOWN,iXPos,iYPos);
            return 0;
        }
        case WM_LBUTTONUP:
        {
            int iXPos = int(LOWORD(lParam));
            int iYPos = int(HIWORD(lParam));
            pkTheApp->OnMousePress(MgcApplication::MGC_MOUSE_LEFT_BUTTON,
                MgcApplication::MOUSE_UP,iXPos,iYPos);
            return 0;
        }
        case WM_MBUTTONDOWN:
        {
            int iXPos = int(LOWORD(lParam));
            int iYPos = int(HIWORD(lParam));
            pkTheApp->OnMousePress(MgcApplication::MGC_MOUSE_MIDDLE_BUTTON,
                MgcApplication::MOUSE_DOWN,iXPos,iYPos);
            return 0;
        }
        case WM_MBUTTONUP:
        {
            int iXPos = int(LOWORD(lParam));
            int iYPos = int(HIWORD(lParam));
            pkTheApp->OnMousePress(MgcApplication::MGC_MOUSE_MIDDLE_BUTTON,
                MgcApplication::MOUSE_UP,iXPos,iYPos);
            return 0;
        }
        case WM_RBUTTONDOWN:
        {
            int iXPos = int(LOWORD(lParam));
            int iYPos = int(HIWORD(lParam));
            pkTheApp->OnMousePress(MgcApplication::MGC_MOUSE_RIGHT_BUTTON,
                MgcApplication::MOUSE_DOWN,iXPos,iYPos);
            return 0;
        }
        case WM_RBUTTONUP:
        {
            int iXPos = int(LOWORD(lParam));
            int iYPos = int(HIWORD(lParam));
            pkTheApp->OnMousePress(MgcApplication::MGC_MOUSE_RIGHT_BUTTON,
                MgcApplication::MOUSE_UP,iXPos,iYPos);
            return 0;
        }
        case WM_MOUSEMOVE:
        {
            int iXPos = int(LOWORD(lParam));
            int iYPos = int(HIWORD(lParam));
            unsigned int uiKeys = (unsigned int)(wParam);
            if ( (uiKeys & MK_LBUTTON)
            ||   (uiKeys & MK_MBUTTON)
            ||   (uiKeys & MK_RBUTTON) )
            {
                pkTheApp->OnMotion(iXPos,iYPos);
            }
            else
            {
                pkTheApp->OnPassiveMotion(iXPos,iYPos);
            }
            return 0;
        }
        case WM_DESTROY:
        {
            PostQuitMessage(0);
            return 0;
        }
    }

    return DefWindowProc(hWnd,uiMsg,wParam,lParam);
}
//---------------------------------------------------------------------------
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE, LPSTR acArgument, int)
{
    MgcApplication* pkTheApp = MgcApplication::GetApplication();
    assert( pkTheApp );
    if ( !pkTheApp )
        return -1;

    if ( acArgument && strlen(acArgument) > 0 )
    {
        char* acProcessed = ProcessCommand(acArgument);
        pkTheApp->SetCommand(new MgcCommand(acProcessed));
        delete[] acProcessed;
    }

    // register the window class
    static char s_acWindowClass[] = "Wild Magic Application";
    WNDCLASS wc;
    wc.style         = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc   = WinProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = LoadIcon(NULL,IDI_APPLICATION);
    wc.hCursor       = LoadCursor(NULL,IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszClassName = s_acWindowClass;
    wc.lpszMenuName = 0;
    RegisterClass(&wc);

    // require the window to have the specified client area
    RECT kRect = { 0, 0, pkTheApp->GetWidth()-1, pkTheApp->GetHeight()-1 };
    AdjustWindowRect(&kRect,WS_OVERLAPPEDWINDOW,false);

    // create the application window
    HWND hWnd = CreateWindow (s_acWindowClass,pkTheApp->GetWindowTitle(),
        WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,
        kRect.right - kRect.left + 1,kRect.bottom - kRect.top + 1,0,0,
        hInstance,0);

    pkTheApp->SetWindowID((int)hWnd);


    pkTheApp->SetRenderer(new MgcOglRenderer(hWnd,pkTheApp->GetWidth(),
        pkTheApp->GetHeight()));

    pkTheApp->SetCamera(new MgcOglCamera(pkTheApp->GetWidth(),
        pkTheApp->GetHeight()));

    if ( !pkTheApp->OnInitialize() )
        return -2;

    // display the window
    ShowWindow(hWnd,SW_SHOW);
    UpdateWindow(hWnd);

    // allow the application to initialize before starting the message pump
    MSG kMsg;
    while ( TRUE )
    {
        if ( PeekMessage(&kMsg,0,0,0,PM_REMOVE) )
        {
            if ( kMsg.message == WM_QUIT )
                break;

            HACCEL hAccel = 0;
            if ( !TranslateAccelerator(hWnd,hAccel,&kMsg) )
            {
                TranslateMessage(&kMsg);
                DispatchMessage(&kMsg);
            }
        }
        else
        {
            pkTheApp->OnIdle();
        }
    }

    pkTheApp->OnTerminate();

    return 0;
}
//---------------------------------------------------------------------------
